package com.rsscollector.util;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.log4j.Logger;
import org.jdom.Document;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;

import java.io.*;
import java.net.URL;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

/**
 * IO.java provides some IO utilities
 */
public class IO {

    private static Logger log = Logger.getLogger(IO.class);
    private static final int BUFFER_SIZE = 2048;

    public static void createFile(String sFile) throws IOException {
        File f = new File(sFile);
        if (!f.exists()) {
            f.createNewFile();
        }
    }

    /**
     * Append data to file
     *
     * @param sFileName
     * @param sLine
     * @throws IOException
     */
    public static void appendString(String sFileName, String sLine)
            throws IOException {
        FileWriter fwr = null;
        try {
            createFile(sFileName);
            fwr = new FileWriter(sFileName, true);
            fwr.write("\n" + sLine);
        } catch (IOException ex) {
            throw ex;
        } finally {
            try {
                fwr.close();
            } catch (Exception ex) {
            }
        }
    }

    /**
     * Close stream
     *
     * @param stm
     */
    public static void close(OutputStream stm) {
        if (stm != null) {
            try {
                stm.close();
            } catch (IOException ex) {
                log.error("Failed to close output stream !!!", ex);
            }
        }
    }

    /**
     * Close stream
     *
     * @param stm
     */
    public static void close(InputStream stm) {
        if (stm != null) {
            try {
                stm.close();
            } catch (IOException ex) {
                log.error("Failed to close input stream !!!", ex);
            }
        }
    }

    /**
     * Open input stream from a string
     *
     * @param str
     * @return InputStream
     */
    public static InputStream openInputStream(final String str) {
        return new ByteArrayInputStream(str.getBytes());
    }

    /**
     * read the content of a file
     *
     * @param url
     * @return
     * @throws IOException
     */
    public static byte[] readFile(URL url) throws IOException {
        InputStream fis = null;
        BufferedInputStream bis = null;
        ByteArrayOutputStream container = new ByteArrayOutputStream();
        byte[] buff = new byte[BUFFER_SIZE];

        log.debug("IO.readFile(" + url + ")");

        try {
            fis = new DataInputStream(url.openConnection().getInputStream());
            bis = new BufferedInputStream(fis);
            int nByteRead = -1;
            while ((nByteRead = bis.read(buff, 0, BUFFER_SIZE)) != -1) {
                container.write(buff, 0, nByteRead);
            }
            container.flush();
            return container.toByteArray();
        } catch (IOException ex) {
            throw ex;
        } finally {
            IO.close(bis);
            IO.close(fis);
            IO.close(container);
        }
    }

    /**
     * read the content of a file
     *
     * @param sFileName
     * @return
     * @throws IOException
     */
    public static byte[] readFile(final String sFileName) throws IOException {
        FileInputStream fis = null;
        BufferedInputStream bis = null;
        ByteArrayOutputStream container = new ByteArrayOutputStream();
        byte[] buff = new byte[BUFFER_SIZE];
        try {
            fis = new FileInputStream(sFileName);
            bis = new BufferedInputStream(fis);
            int nByteRead = -1;
            while ((nByteRead = bis.read(buff, 0, BUFFER_SIZE)) != -1) {
                container.write(buff, 0, nByteRead);
            }
            container.flush();
            return container.toByteArray();
        } catch (IOException ex) {
            throw ex;
        } finally {
            IO.close(bis);
            IO.close(fis);
            IO.close(container);
        }
    }

    /**
     * read data from URL
     *
     * @param url
     * @return
     * @throws IOException
     */
    public static byte[] readURL(URL url) throws IOException {
        InputStream fis = null;
        BufferedInputStream bis = null;
        ByteArrayOutputStream container = new ByteArrayOutputStream();
        byte[] buff = new byte[BUFFER_SIZE];

        try {
            fis = new DataInputStream(url.openStream());
            bis = new BufferedInputStream(fis);
            int nByteRead = -1;
            while ((nByteRead = bis.read(buff, 0, BUFFER_SIZE)) != -1) {
                container.write(buff, 0, nByteRead);
            }
            container.flush();
            return container.toByteArray();
        } catch (IOException ex) {
            throw ex;
        } finally {
            bis.close();
            fis.close();
            container.close();
        }
    }

    /**
     * Read version file
     *
     * @param sPath
     * @return
     * @throws IOException
     */
    public static String readVersion(String sPath) throws IOException {


        String sLine = null;
        String sKeep = null;

        FileInputStream fis = null;
        DataInputStream din = null;

        try {
            fis = new FileInputStream(sPath);
            din = new DataInputStream(fis);

            while ((sLine = din.readLine()) != null) {

                int nEqualIndex = sLine.indexOf("=");

                if (nEqualIndex < 0) {
                    continue;
                }

                sKeep = sLine;
                String sTemp = sLine.toLowerCase();

                int nVersionIndex = sTemp.indexOf("version");

                if (nVersionIndex < 0) {
                    continue;
                }

                return sLine.substring(nEqualIndex + 1).trim();
            }
        } catch (IOException ex) {
            throw ex;
        } finally {
            IO.close(din);
            IO.close(fis);
        }


        if (sKeep != null) {
            int nEqualIndex = sKeep.indexOf("=");
            return sKeep.substring(nEqualIndex + 1).trim();
        }

        return "";
    }

    /**
     * read the content of a file
     *
     * @param is
     * @return
     * @throws IOException
     */
    public static byte[] readStream(InputStream is) throws IOException {
        BufferedInputStream bis = null;
        ByteArrayOutputStream container = new ByteArrayOutputStream();
        byte[] buff = new byte[BUFFER_SIZE];
        try {
            bis = new BufferedInputStream(is);
            int nByteRead = -1;
            while ((nByteRead = bis.read(buff, 0, BUFFER_SIZE)) != -1) {
                container.write(buff, 0, nByteRead);
            }
            container.flush();
            return container.toByteArray();
        } catch (IOException ex) {
            throw ex;
        } finally {
            IO.close(bis);
            IO.close(container);
        }
    }

    /**
     * check if the file exists
     *
     * @param sFilePath
     * @return boolean
     */
    public static boolean exist(final String sFilePath) {
        File f = new File(sFilePath);
        return f.exists();
    }

    /**
     * delete a file
     *
     * @param sPath
     */
    public static void remove(final String sPath) {
        if (!IO.exist(sPath)) {
            return;
        }
        File f = new File(sPath);
        if (f.isFile()) {
            if (f.getName().equals(".") || f.getName().equals("..")) {
                return;
            }
            f.delete();
        } else {
            File[] list = f.listFiles();
            for (int i = 0; i < list.length; i++) {
                File one = list[i];
                IO.remove(one.getAbsolutePath());
            }
            f.delete();
        }
    }

    /**
     * create a file with content
     * on hard disk
     *
     * @param sFileName
     * @param data
     * @throws IOException
     */
    public static void writeFile(final String sFileName, final byte[] data)
            throws IOException {
        FileOutputStream fos = null;
        BufferedOutputStream bos = null;
        try {
            fos = new FileOutputStream(sFileName);
            bos = new BufferedOutputStream(fos);
            int length = data.length;
            bos.write(data, 0, length);
            bos.flush();
        } catch (IOException ex) {
            throw ex;
        } finally {
            IO.close(bos);
            IO.close(fos);
        }
    }

    /**
     * create a file and
     * put it onto a folder
     *
     * @param sFilePath
     * @param data
     * @throws IOException
     */
    public static void createFileWithFolder(final String sFilePath,
                                            final byte[] data) throws IOException {
        String sDir = getFolder(sFilePath);
        IO.createDirs(sDir);
        try {
            IO.writeFile(sFilePath, data);
        } catch (IOException ex) {
            throw ex;
        }
    }

    /**
     * create directories
     *
     * @param sDirPath
     */
    public static void createDirs(final String sDirPath) {
        File file = new File(sDirPath);
        if (file.exists()) {
            return;
        }
        file.mkdirs();
    }

    /**
     * convert an object to byte arrray
     */
    public static byte[] objectToBytes(final Serializable obj)
            throws IOException {
        ByteArrayOutputStream byteStm = new ByteArrayOutputStream();
        ObjectOutputStream objStm = null;
        try {
            objStm = new ObjectOutputStream(byteStm);
            objStm.writeObject(obj);
            objStm.flush();
        } catch (IOException ex) {
            throw ex;
        } finally {
            IO.close(objStm);
            IO.close(byteStm);
        }
        byte[] objBytes = byteStm.toByteArray();
        return objBytes;
    }

    /**
     * convert a byte array to an object
     *
     * @param bytes
     * @return
     * @throws IOException
     * @throws ClassNotFoundException
     */
    public static Object bytesToObject(final byte[] bytes) throws IOException,
            ClassNotFoundException {
        ByteArrayInputStream in = new ByteArrayInputStream(bytes);
        ObjectInputStream objIn = null;
        Object rs = null;
        try {
            objIn = new ObjectInputStream(in);
            rs = objIn.readObject();
        } catch (IOException ex) {
            throw ex;
        } catch (ClassNotFoundException ex) {
            throw ex;
        } finally {
            IO.close(objIn);
            IO.close(in);
        }
        return rs;
    }

    /**
     * read content of a text file
     *
     * @param sFileName
     * @return
     * @throws IOException
     */
    public static String readTextFile(final String sFileName)
            throws IOException {
        FileInputStream fis = null;
        DataInputStream din = null;
        String content = "";
        try {
            fis = new FileInputStream(sFileName);
            din = new DataInputStream(fis);
            String line = null;
            while ((line = din.readLine()) != null) {
                content += line + "\n";
            }
            return content;
        } catch (IOException ex) {
            throw ex;
        } finally {
            IO.close(din);
            IO.close(fis);
        }
    }

    /**
     * Get folder path from
     * a absolute file name
     * @param sFileName file name
     * @return folder path
     */
    /*public static String getFolder(final String sFileName) {
         File file = new File(sFileName);
         String sParent = file.getParent();
         return sParent;
     }*/

    /**
     * write a text file on drive
     *
     * @param sFileName
     * @param sContent
     * @throws IOException
     */
    public static void writeTextFile(final String sFileName,
                                     final String sContent) throws IOException {
        FileOutputStream fos = null;
        DataOutputStream dos = null;
        try {
            fos = new FileOutputStream(sFileName);
            dos = new DataOutputStream(fos);
            dos.writeChars(sContent);
            dos.flush();
        } catch (IOException ex) {
            throw ex;
        } finally {
            IO.close(dos);
            IO.close(fos);
        }
    }

    /**
     * add byte array to a zip file
     *
     * @param sName
     * @param data
     * @param zipOut
     * @throws IOException
     */
    public static void addByteToZip(final String sName, final byte[] data,
                                    ZipOutputStream zipOut) throws IOException {
        ZipEntry entry = new ZipEntry(sName);
        try {
            zipOut.putNextEntry(entry);
            zipOut.write(data, 0, data.length);
            zipOut.flush();
        } catch (IOException ex) {
            throw ex;
        }
    }

    /**
     * Read a file from an URL
     *
     * @param url
     * @return a document object
     * @throws JDOMException IOException, Exception
     */
    public static Document readFileFromURL(final URL url) throws JDOMException, IOException, Exception {
        SAXBuilder builder = null;
        Document document = null;
        try {
            log.info("START readFileFromURL:\t" + url.getPath());
            builder = new SAXBuilder(false);
            document = builder.build(url);
            log.info("END readFileFromURL");
        } catch (JDOMException jdex) {
            throw jdex;
        } catch (IOException ioe) {
            throw ioe;
        } catch (Exception ex) {
            throw ex;
        }
        return document;
    }

    /**
     * Read a file from a path
     *
     * @param filePath
     * @return a document object
     * @throws JDOMException IOException Exception
     */
    public static Document readFileFromPath(final String filePath) throws JDOMException, IOException, Exception {
        SAXBuilder builder = null;
        Document document = null;
        try {
            log.info("START readFileFromPath:\t" + filePath);
            builder = new SAXBuilder(false);
            document = builder.build(filePath);
            log.info("END readFileFromPath");
        } catch (JDOMException jdex) {
            throw jdex;
        } catch (IOException ioe) {
            throw ioe;
        } catch (Exception ex) {
            throw ex;
        }
        return document;
    }

    public static boolean isFile(String sPath) {
        File file = new File(sPath);
        return file.isFile();
    }

    public static byte[] zipFolder(final String sParent) throws IOException {

        ByteArrayOutputStream container = null;
        ZipOutputStream zipOut = null;

        try {

            container = new ByteArrayOutputStream();
            zipOut = new ZipOutputStream(container);

            File file = new File(sParent);
            String[] childs = file.list();

            if (childs == null || childs.length == 0) {
                throw new IOException("Folder is empty");
            }

            for (int i = 0; i < childs.length; i++) {
                addFileToZipRecursive(sParent, childs[i], zipOut);
            }

        } catch (IOException ex) {
            throw ex;
        } finally {
            IO.close(zipOut);
            IO.close(container);
        }
        return container.toByteArray();
    }

    private static void addFileToZipRecursive(String sParent, String sFileName,
                                              ZipOutputStream zipOut) throws IOException {

        byte data[] = new byte[BUFFER_SIZE];
        BufferedInputStream bis = null;
        FileInputStream is = null;
        String sCurrent = sParent + "/" + sFileName;

        if (IO.isFile(sCurrent)) {

            try {

                is = new FileInputStream(sCurrent);
                bis = new BufferedInputStream(is, BUFFER_SIZE);
                ZipEntry entry = new ZipEntry(sFileName);
                zipOut.putNextEntry(entry);
                int count = -1;

                while ((count = bis.read(data, 0, BUFFER_SIZE)) != -1) {
                    zipOut.write(data, 0, count);
                }

                zipOut.flush();

            } catch (IOException ex) {
                throw ex;
            } finally {
                IO.close(bis);
                IO.close(is);
            }

        } else {

            File file = new File(sCurrent);
            String[] childs = file.list();
            try {
                for (int i = 0; i < childs.length; i++) {
                    addFileToZipRecursive(sParent, sFileName + "/" + childs[i],
                            zipOut);
                }
            } catch (IOException ex) {
                throw ex;
            }
        }
    }

    /**
     * Unzip one file
     *
     * @param bZipData
     * @param sExtension
     * @return
     * @throws IOException
     */
    public static byte[] unzipFirstFileWithExtension(final byte[] bZipData,
                                                     final String sExtension) throws IOException {

        ZipInputStream zis = null;
        BufferedInputStream bis = null;
        ByteArrayInputStream bais = null;

        try {
            bais = new ByteArrayInputStream(bZipData);
            bis = new BufferedInputStream(bais);
            zis = new ZipInputStream(bis);
            ZipEntry entry = null;

            while ((entry = zis.getNextEntry()) != null) {
                final String sCurrentName = entry.getName();

                // filter and get first file with sExt extension 
                if (!(sCurrentName == null || sCurrentName.trim().length() == 0)) {
                    String sTemp = sCurrentName.trim().toLowerCase();
                    if (sTemp.endsWith("." + sExtension)) {
                        return readStream(zis);
                    }
                }
            }
        } catch (IOException ex) {
            throw ex;
        } finally {
            IO.close(zis);
            IO.close(bis);
            IO.close(bais);
        }
        return null;
    }

    /**
     * Get the file name from a absolutely file path
     *
     * @param sPath
     * @return String
     */
    public static String getFolder(final String sPath) {
        int index1 = sPath.lastIndexOf('/');
        int index2 = sPath.lastIndexOf('\\');
        int index = 0;
        if (index1 < index2) {
            index = index2;
        } else if (index1 > 0) {
            index = index1;
        }

        if (index > 0) {
            return sPath.substring(0, index);
        } else {
            return sPath.substring(0);
        }
    }

    public static String getContentFromURL(String url) {
        HttpClient client = new HttpClient();

        PostMethod httppost = new PostMethod(url);
        httppost.setRequestHeader("Content-Type", "text/plain; charset=UTF-8");;
        try {
            client.executeMethod(httppost);

            if (httppost.getStatusCode() == HttpStatus.SC_OK) {
                return httppost.getResponseBodyAsString();
            } else {
                log.warn("Unexpected failure: " + url + " with error status" +httppost.getStatusLine().toString());
                System.out.println("Unexpected failure: " + url + " with error status" +httppost.getStatusLine().toString());
            }
        } catch (IOException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        } finally {
            httppost.releaseConnection();
        }
        return null;
    }
}


